home *** CD-ROM | disk | FTP | other *** search
/ AGA Toolkit '97 / The AGA Toolkit '97.iso / miscellaneous / science / maths / calc / source / qio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-07  |  12.8 KB  |  672 lines

  1. /*
  2.  * Copyright (c) 1994 David I. Bell
  3.  * Permission is granted to use, distribute, or modify this source,
  4.  * provided that this copyright notice remains intact.
  5.  *
  6.  * Scanf and printf routines for arbitrary precision rational numbers
  7.  */
  8.  
  9. #include "stdarg.h"
  10. #include "qmath.h"
  11.  
  12.  
  13. #define    PUTCHAR(ch)        math_chr(ch)
  14. #define    PUTSTR(str)        math_str(str)
  15. #define    PRINTF1(fmt, a1)    math_fmt(fmt, a1)
  16. #define    PRINTF2(fmt, a1, a2)    math_fmt(fmt, a1, a2)
  17.  
  18. int tilde_ok = TRUE;    /* FALSE => don't print '~' for rounded value */
  19.  
  20. #if 0
  21. static long    etoalen;
  22. static char    *etoabuf = NULL;
  23. #endif
  24.  
  25. static long    scalefactor;
  26. static ZVALUE    scalenumber = { 0, 0, 0 };
  27.  
  28.  
  29. /*
  30.  * Print a formatted string containing arbitrary numbers, similar to printf.
  31.  * ALL numeric arguments to this routine are rational NUMBERs.
  32.  * Various forms of printing such numbers are supplied, in addition
  33.  * to strings and characters.  Output can actually be to any FILE
  34.  * stream or a string.
  35.  */
  36. #ifdef VARARGS
  37. # define VA_ALIST1 fmt, va_alist
  38. # define VA_DCL1 char *fmt; va_dcl
  39. #else
  40. # if defined(__STDC__) && __STDC__ == 1
  41. #  define VA_ALIST1 char *fmt, ...
  42. #  define VA_DCL1
  43. # else
  44. #  define VA_ALIST1 fmt
  45. #  define VA_DCL1 char *fmt;
  46. # endif
  47. #endif
  48. /*VARARGS*/
  49. void
  50. qprintf(VA_ALIST1)
  51.     VA_DCL1
  52. {
  53.     va_list ap;
  54.     NUMBER *q;
  55.     int ch, sign;
  56.     long width, precision;
  57.  
  58. #ifdef VARARGS
  59.     va_start(ap);
  60. #else
  61.     va_start(ap, fmt);
  62. #endif
  63.     while ((ch = *fmt++) != '\0') {
  64.         if (ch == '\\') {
  65.             ch = *fmt++;
  66.             switch (ch) {
  67.                 case 'n': ch = '\n'; break;
  68.                 case 'r': ch = '\r'; break;
  69.                 case 't': ch = '\t'; break;
  70.                 case 'f': ch = '\f'; break;
  71.                 case 'v': ch = '\v'; break;
  72.                 case 'b': ch = '\b'; break;
  73.                 case 0:
  74.                     va_end(ap);
  75.                     return;
  76.             }
  77.             PUTCHAR(ch);
  78.             continue;
  79.         }
  80.         if (ch != '%') {
  81.             PUTCHAR(ch);
  82.             continue;
  83.         }
  84.         ch = *fmt++;
  85.         width = 0; precision = 8; sign = 1;
  86. percent:    ;
  87.         switch (ch) {
  88.             case 'd':
  89.                 q = va_arg(ap, NUMBER *);
  90.                 qprintfd(q, width);
  91.                 break;
  92.             case 'f':
  93.                 q = va_arg(ap, NUMBER *);
  94.                 qprintff(q, width, precision);
  95.                 break;
  96.             case 'e':
  97.                 q = va_arg(ap, NUMBER *);
  98.                 qprintfe(q, width, precision);
  99.                 break;
  100.             case 'r':
  101.             case 'R':
  102.                 q = va_arg(ap, NUMBER *);
  103.                 qprintfr(q, width, (BOOL) (ch == 'R'));
  104.                 break;
  105.             case 'N':
  106.                 q = va_arg(ap, NUMBER *);
  107.                 zprintval(q->num, 0L, width);
  108.                 break;
  109.             case 'D':
  110.                 q = va_arg(ap, NUMBER *);
  111.                 zprintval(q->den, 0L, width);
  112.                 break;
  113.             case 'o':
  114.                 q = va_arg(ap, NUMBER *);
  115.                 qprintfo(q, width);
  116.                 break;
  117.             case 'x':
  118.                 q = va_arg(ap, NUMBER *);
  119.                 qprintfx(q, width);
  120.                 break;
  121.             case 'b':
  122.                 q = va_arg(ap, NUMBER *);
  123.                 qprintfb(q, width);
  124.                 break;
  125.             case 's':
  126.                 PUTSTR(va_arg(ap, char *));
  127.                 break;
  128.             case 'c':
  129.                 PUTCHAR(va_arg(ap, int));
  130.                 break;
  131.             case 0:
  132.                 va_end(ap);
  133.                 return;
  134.             case '-':
  135.                 sign = -1;
  136.                 ch = *fmt++;
  137.             default:
  138.         if (('0' <= ch && ch <= '9') || ch == '.' || ch == '*') {
  139.             if (ch == '*') {
  140.                 q = va_arg(ap, NUMBER *);
  141.                 width = sign * qtoi(q);
  142.                 ch = *fmt++;
  143.             } else if (ch != '.') {
  144.                 width = ch - '0';
  145.                 while ('0' <= (ch = *fmt++) && ch <= '9')
  146.                     width = width * 10 + ch - '0';
  147.                 width *= sign;
  148.             }
  149.             if (ch == '.') {
  150.                 if ((ch = *fmt++) == '*') {
  151.                     q = va_arg(ap, NUMBER *);
  152.                     precision = qtoi(q);
  153.                     ch = *fmt++;
  154.                 } else {
  155.                     precision = 0;
  156.                     while ('0' <= (ch = *fmt++) && ch <= '9')
  157.                         precision = precision * 10 + ch - '0';
  158.                 }
  159.             }
  160.             goto percent;
  161.         }
  162.         }
  163.     }
  164.     va_end(ap);
  165. }
  166.  
  167.  
  168. #if 0
  169. /*
  170.  * Read a number from the specified FILE stream (NULL means stdin).
  171.  * The number can be an integer, a fraction, a real number, an
  172.  * exponential number, or a hex, octal or binary number.  Leading blanks
  173.  * are skipped.  Illegal numbers return NULL.  Unrecognized characters
  174.  * remain to be read on the line.
  175.  *    q = qreadval(fp);
  176.  */
  177. NUMBER *
  178. qreadval(fp)
  179.     FILE *fp;        /* file stream to read from (or NULL) */
  180. {
  181.     NUMBER *r;        /* returned number */
  182.     char *cp;         /* current buffer location */
  183.     long savecc;        /* characters saved in buffer */
  184.     long scancc;         /* characters parsed correctly */
  185.     int ch;            /* current character */
  186.  
  187.     if (fp == NULL)
  188.         fp = stdin;
  189.     if (etoabuf == NULL) {
  190.         etoabuf = (char *)malloc(OUTBUFSIZE + 2);
  191.         if (etoabuf == NULL)
  192.             return NULL;
  193.         etoalen = OUTBUFSIZE;
  194.     }
  195.     cp = etoabuf;
  196.     ch = fgetc(fp);
  197.     while ((ch == ' ') || (ch == '\t'))
  198.         ch = fgetc(fp);
  199.     savecc = 0;
  200.     for (;;) {
  201.         if (ch == EOF)
  202.             return NULL;
  203.         if (savecc >= etoalen)
  204.         {
  205.             cp = (char *)realloc(etoabuf, etoalen + OUTBUFSIZE + 2);
  206.             if (cp == NULL)
  207.                 return NULL;
  208.             etoabuf = cp;
  209.             etoalen += OUTBUFSIZE;
  210.             cp += savecc;
  211.         }
  212.         *cp++ = (char)ch;
  213.         *cp = '\0';
  214.         scancc = qparse(etoabuf, QPF_SLASH);
  215.         if (scancc != ++savecc)
  216.             break;
  217.         ch = fgetc(fp);
  218.     }
  219.     ungetc(ch, fp);
  220.     if (scancc < 0)
  221.         return NULL;
  222.     r = atoq(etoabuf);
  223.     if (ziszero(r->den)) {
  224.         qfree(r);
  225.         r = NULL;
  226.     }
  227.     return r;
  228. }
  229. #endif
  230.  
  231.  
  232. /*
  233.  * Print a number in the specified output mode.
  234.  * If MODE_DEFAULT is given, then the default output mode is used.
  235.  * Any approximate output is flagged with a leading tilde.
  236.  * Integers are always printed as themselves.
  237.  */
  238. void
  239. qprintnum(q, outmode)
  240.     int outmode;
  241.     NUMBER *q;
  242. {
  243.     NUMBER tmpval;
  244.     long prec, exp;
  245.  
  246.     if (outmode == MODE_DEFAULT)
  247.         outmode = _outmode_;
  248.     if ((outmode == MODE_FRAC) || ((outmode == MODE_REAL) && qisint(q))) {
  249.         qprintfr(q, 0L, FALSE);
  250.         return;
  251.     }
  252.     switch (outmode) {
  253.         case MODE_INT:
  254.             if (tilde_ok && qisfrac(q))
  255.                 PUTCHAR('~');
  256.             qprintfd(q, 0L);
  257.             break;
  258.  
  259.         case MODE_REAL:
  260.             prec = qplaces(q);
  261.             if ((prec < 0) || (prec > _outdigits_)) {
  262.                 prec = _outdigits_;
  263.                 if (tilde_ok) {
  264.                     PUTCHAR('~');
  265.                 }
  266.             }
  267.             qprintff(q, 0L, prec);
  268.             break;
  269.  
  270.         case MODE_EXP:
  271.             if (qiszero(q)) {
  272.                 PUTCHAR('0');
  273.                 return;
  274.             }
  275.             tmpval = *q;
  276.             tmpval.num.sign = 0;
  277.             exp = qilog10(&tmpval);
  278.             if (exp == 0) {        /* in range to output as real */
  279.                 qprintnum(q, MODE_REAL);
  280.                 return;
  281.             }
  282.             tmpval.num = _one_;
  283.             tmpval.den = _one_;
  284.             if (exp > 0)
  285.                 ztenpow(exp, &tmpval.den);
  286.             else
  287.                 ztenpow(-exp, &tmpval.num);
  288.             q = qmul(q, &tmpval);
  289.             zfree(tmpval.num);
  290.             zfree(tmpval.den);
  291.             qprintnum(q, MODE_REAL);
  292.             qfree(q);
  293.             PRINTF1("e%ld", exp);
  294.             break;
  295.  
  296.         case MODE_HEX:
  297.             qprintfx(q, 0L);
  298.             break;
  299.  
  300.         case MODE_OCTAL:
  301.             qprintfo(q, 0L);
  302.             break;
  303.  
  304.         case MODE_BINARY:
  305.             qprintfb(q, 0L);
  306.             break;
  307.  
  308.         default:
  309.             math_error("Bad mode for print");
  310.     }
  311. }
  312.  
  313.  
  314. /*
  315.  * Print a number in floating point representation.
  316.  * Example:  193.784
  317.  */
  318. void
  319. qprintff(q, width, precision)
  320.     NUMBER *q;
  321.     long width;
  322.     long precision;
  323. {
  324.     ZVALUE z, z1;
  325.  
  326.     if (precision != scalefactor) {
  327.         if (scalenumber.v)
  328.             zfree(scalenumber);
  329.         ztenpow(precision, &scalenumber);
  330.         scalefactor = precision;
  331.     }
  332.     if (scalenumber.v)
  333.         zmul(q->num, scalenumber, &z);
  334.     else
  335.         z = q->num;
  336.     if (qisfrac(q)) {
  337.         zquo(z, q->den, &z1);
  338.         if (z.v != q->num.v)
  339.             zfree(z);
  340.         z = z1;
  341.     }
  342.     if (qisneg(q) && ziszero(z))
  343.         PUTCHAR('-');
  344.     zprintval(z, precision, width);
  345.     if (z.v != q->num.v)
  346.         zfree(z);
  347. }
  348.  
  349.  
  350. /*
  351.  * Print a number in exponential notation.
  352.  * Example: 4.1856e34
  353.  */
  354. /*ARGSUSED*/
  355. void
  356. qprintfe(q, width, precision)
  357.     register NUMBER *q;
  358.     long width;
  359.     long precision;
  360. {
  361.     long exponent;
  362.     NUMBER q2;
  363.     ZVALUE num, den, tenpow, tmp;
  364.  
  365.     if (qiszero(q)) {
  366.         PUTSTR("0.0");
  367.         return;
  368.     }
  369.     num = q->num;
  370.     den = q->den;
  371.     num.sign = 0;
  372.     exponent = zdigits(num) - zdigits(den);
  373.     if (exponent > 0) {
  374.         ztenpow(exponent, &tenpow);
  375.         zmul(den, tenpow, &tmp);
  376.         zfree(tenpow);
  377.         den = tmp;
  378.     }
  379.     if (exponent < 0) {
  380.         ztenpow(-exponent, &tenpow);
  381.         zmul(num, tenpow, &tmp);
  382.         zfree(tenpow);
  383.         num = tmp;
  384.     }
  385.     if (zrel(num, den) < 0) {
  386.         zmuli(num, 10L, &tmp);
  387.         if (num.v != q->num.v)
  388.             zfree(num);
  389.         num = tmp;
  390.         exponent--;
  391.     }
  392.     q2.num = num;
  393.     q2.den = den;
  394.     q2.num.sign = q->num.sign;
  395.     qprintff(&q2, 0L, precision);
  396.     if (exponent)
  397.         PRINTF1("e%ld", exponent);
  398.     if (num.v != q->num.v)
  399.         zfree(num);
  400.     if (den.v != q->den.v)
  401.         zfree(den);
  402. }
  403.  
  404.  
  405. /*
  406.  * Print a number in rational representation.
  407.  * Example: 397/37
  408.  */
  409. void
  410. qprintfr(q, width, force)
  411.     NUMBER *q;
  412.     long width;
  413.     BOOL force;
  414. {
  415.     zprintval(q->num, 0L, width);
  416.     if (force || qisfrac(q)) {
  417.         PUTCHAR('/');
  418.         zprintval(q->den, 0L, width);
  419.     }
  420. }
  421.  
  422.  
  423. /*
  424.  * Print a number as an integer (truncating fractional part).
  425.  * Example: 958421
  426.  */
  427. void
  428. qprintfd(q, width)
  429.     NUMBER *q;
  430.     long width;
  431. {
  432.     ZVALUE z;
  433.  
  434.     if (qisfrac(q)) {
  435.         zquo(q->num, q->den, &z);
  436.         zprintval(z, 0L, width);
  437.         zfree(z);
  438.     } else
  439.         zprintval(q->num, 0L, width);
  440. }
  441.  
  442.  
  443. /*
  444.  * Print a number in hex.
  445.  * This prints the numerator and denominator in hex.
  446.  */
  447. void
  448. qprintfx(q, width)
  449.     NUMBER *q;
  450.     long width;
  451. {
  452.     zprintx(q->num, width);
  453.     if (qisfrac(q)) {
  454.         PUTCHAR('/');
  455.         zprintx(q->den, 0L);
  456.     }
  457. }
  458.  
  459.  
  460. /*
  461.  * Print a number in binary.
  462.  * This prints the numerator and denominator in binary.
  463.  */
  464. void
  465. qprintfb(q, width)
  466.     NUMBER *q;
  467.     long width;
  468. {
  469.     zprintb(q->num, width);
  470.     if (qisfrac(q)) {
  471.         PUTCHAR('/');
  472.         zprintb(q->den, 0L);
  473.     }
  474. }
  475.  
  476.  
  477. /*
  478.  * Print a number in octal.
  479.  * This prints the numerator and denominator in octal.
  480.  */
  481. void
  482. qprintfo(q, width)
  483.     NUMBER *q;
  484.     long width;
  485. {
  486.     zprinto(q->num, width);
  487.     if (qisfrac(q)) {
  488.         PUTCHAR('/');
  489.         zprinto(q->den, 0L);
  490.     }
  491. }
  492.  
  493.  
  494. /*
  495.  * Convert a string to a number in rational, floating point,
  496.  * exponential notation, hex, or octal.
  497.  *    q = atoq(string);
  498.  */
  499. NUMBER *
  500. atoq(s)
  501.     register char *s;
  502. {
  503.     register NUMBER *q;
  504.     register char *t;
  505.     ZVALUE div, newnum, newden, tmp;
  506.     long decimals, exp;
  507.     BOOL hex, negexp;
  508.  
  509.     q = qalloc();
  510.     decimals = 0;
  511.     exp = 0;
  512.     negexp = FALSE;
  513.     hex = FALSE;
  514.     t = s;
  515.     if ((*t == '+') || (*t == '-'))
  516.         t++;
  517.     if ((*t == '0') && ((t[1] == 'x') || (t[1] == 'X'))) {
  518.         hex = TRUE;
  519.         t += 2;
  520.     }
  521.     while (((*t >= '0') && (*t <= '9')) || (hex &&
  522.         (((*t >= 'a') && (*t <= 'f')) || ((*t >= 'A') && (*t <= 'F')))))
  523.             t++;
  524.     if (*t == '/') {
  525.         t++;
  526.         atoz(t, &q->den);
  527.     } else if ((*t == '.') || (*t == 'e') || (*t == 'E')) {
  528.         if (*t == '.') {
  529.             t++;
  530.             while ((*t >= '0') && (*t <= '9')) {
  531.                 t++;
  532.                 decimals++;
  533.             }
  534.         }
  535.         /*
  536.          * Parse exponent if any
  537.          */
  538.         if ((*t == 'e') || (*t == 'E')) {
  539.             t++;
  540.             if (*t == '+')
  541.                 t++;
  542.             else if (*t == '-') {
  543.                 negexp = TRUE;
  544.                 t++;
  545.             }
  546.             while ((*t >= '0') && (*t <= '9')) {
  547.                 exp = (exp * 10) + *t++ - '0';
  548.                 if (exp > 1000000)
  549.                     math_error("Exponent too large");
  550.             }
  551.         }
  552.         ztenpow(decimals, &q->den);
  553.     }
  554.     atoz(s, &q->num);
  555.     if (qiszero(q)) {
  556.         qfree(q);
  557.         return qlink(&_qzero_);
  558.     }
  559.     /*
  560.      * Apply the exponential if any
  561.      */
  562.     if (exp) {
  563.         ztenpow(exp, &tmp);
  564.         if (negexp) {
  565.             zmul(q->den, tmp, &newden);
  566.             zfree(q->den);
  567.             q->den = newden;
  568.         } else {
  569.             zmul(q->num, tmp, &newnum);
  570.             zfree(q->num);
  571.             q->num = newnum;
  572.         }
  573.         zfree(tmp);
  574.     }
  575.     /*
  576.      * Reduce the fraction to lowest terms
  577.      */
  578.     if (zisunit(q->num) || zisunit(q->den))
  579.         return q;
  580.     zgcd(q->num, q->den, &div);
  581.     if (zisunit(div))
  582.         return q;
  583.     zquo(q->num, div, &newnum);
  584.     zfree(q->num);
  585.     zquo(q->den, div, &newden);
  586.     zfree(q->den);
  587.     q->num = newnum;
  588.     q->den = newden;
  589.     return q;
  590. }
  591.  
  592.  
  593. /*
  594.  * Parse a number in any of the various legal forms, and return the count
  595.  * of characters that are part of a legal number.  Numbers can be either a
  596.  * decimal integer, possibly two decimal integers separated with a slash, a
  597.  * floating point or exponential number, a hex number beginning with "0x",
  598.  * a binary number beginning with "0b", or an octal number beginning with "0".
  599.  * The flags argument modifies the end of number testing for ease in handling
  600.  * fractions or complex numbers.  Minus one is returned if the number format
  601.  * is definitely illegal.
  602.  */
  603. long
  604. qparse(cp, flags)
  605.     int flags;
  606.     register char *cp;
  607. {
  608.     char *oldcp;
  609.  
  610.     oldcp = cp;
  611.     if ((*cp == '+') || (*cp == '-'))
  612.         cp++;
  613.     if ((*cp == '+') || (*cp == '-'))
  614.         return -1;
  615.     if ((*cp == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) {    /* hex */
  616.         cp += 2;
  617.         while (((*cp >= '0') && (*cp <= '9')) ||
  618.             ((*cp >= 'a') && (*cp <= 'f')) ||
  619.             ((*cp >= 'A') && (*cp <= 'F')))
  620.                 cp++;
  621.         goto done;
  622.     }
  623.     if ((*cp == '0') && ((cp[1] == 'b') || (cp[1] == 'B'))) {    /* binary */
  624.         cp += 2;
  625.         while ((*cp == '0') || (*cp == '1'))
  626.             cp++;
  627.         goto done;
  628.     }
  629.     if ((*cp == '0') && (cp[1] >= '0') && (cp[1] <= '9')) { /* octal */
  630.         while ((*cp >= '0') && (*cp <= '7'))
  631.             cp++;
  632.         goto done;
  633.     }
  634.     /*
  635.      * Number is decimal, but can still be a fraction or real or exponential.
  636.      */
  637.     while ((*cp >= '0') && (*cp <= '9'))
  638.         cp++;
  639.     if (*cp == '/' && flags & QPF_SLASH) {    /* fraction */
  640.         cp++;
  641.         while ((*cp >= '0') && (*cp <= '9'))
  642.             cp++;
  643.         goto done;
  644.     }
  645.     if (*cp == '.') {    /* floating point */
  646.         cp++;
  647.         while ((*cp >= '0') && (*cp <= '9'))
  648.             cp++;
  649.     }
  650.     if ((*cp == 'e') || (*cp == 'E')) {    /* exponential */
  651.         cp++;
  652.         if ((*cp == '+') || (*cp == '-'))
  653.             cp++;
  654.         if ((*cp == '+') || (*cp == '-'))
  655.             return -1;
  656.         while ((*cp >= '0') && (*cp <= '9'))
  657.             cp++;
  658.     }
  659.  
  660. done:
  661.     if (((*cp == 'i') || (*cp == 'I')) && (flags & QPF_IMAG))
  662.         cp++;
  663.     if ((*cp == '.') || ((*cp == '/') && (flags & QPF_SLASH)) ||
  664.         ((*cp >= '0') && (*cp <= '9')) ||
  665.         ((*cp >= 'a') && (*cp <= 'z')) ||
  666.         ((*cp >= 'A') && (*cp <= 'Z')))
  667.             return -1;
  668.     return (cp - oldcp);
  669. }
  670.  
  671. /* END CODE */
  672.